Skip to content

fix(extractor): compile vanilla-extract theme contracts across modules - #761

Open
owjs3901 wants to merge 114 commits into
mainfrom
fix/vanilla-extract-themes
Open

owjs3901 wants to merge 114 commits into
mainfrom
fix/vanilla-extract-themes

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Refs #690 (D: vanilla-extract 테마·모듈 값), #693 (K: DOC-09). 이 PR은 main 대상이며 #757 및 그 기반 PR들이 먼저 병합되어야 합니다. 정확한 #756 f2fc4517과 #757 78ef6602를 각각 두 부모 병합 커밋으로 포함했고 #757 병합은 소스 트리를 바꾸지 않았습니다. 자동 병합하지 않습니다.

요약

중첩 테마 계약과 생성된 변수, 다른 모듈의 스타일·테마 값·상수, 혼합 application 모듈의 정확한 styling slice를 동일한 build-time 경로로 처리합니다. 필요한 helper와 imported producer는 demand-selected view로 평가하고 관련 없는 JSX·browser 접근·runtime 초기화는 실행하지 않습니다. styling runtime, initializer 재실행, consumer로 producer 스타일을 합치는 방식, filename-only result cache는 추가하지 않았습니다.

동작

  • createThemeContract, 두 createTheme overload, createGlobalTheme, createGlobalThemeContract, assignVars, createVar, fallbackVar의 중첩 계약·값·변수 참조를 일치시킵니다. 동일한 입력은 .ts, .tsx, .js, .jsx, .mjs, .css.ts, .css.js에서 동일한 CSS를 생성합니다.
  • imported base는 원본 producer의 typed atom 및 selector identity를 사용합니다. style([base, later])는 property·selector·breakpoint·layer별로 뒤 값이 앞 값을 대체하고, 대체된 앞 선언의 class를 해당 요소에 남기지 않습니다. 다른 요소가 사용할 producer 원본 CSS는 원래 owner에 남습니다.
  • ./theme.css가 실제 .css.ts/.css.js를 가리키는 평가 및 source graph 해석을 맞췄습니다. named/default/aliased/star/namespace export와 exact static key를 통한 API provenance를 추적하며 원본 runtime/CSS import edge를 보존합니다.
  • helper/producer diamond의 필요한 lexical·property demand를 정의 전에 합칩니다. live binding, TDZ, callable cycle, descriptor·prototype·shared DAG·retained mutation 및 source/prefix freshness를 보존합니다. 쓰지 않는 browser sibling을 평가하거나 필요한 값을 빈 CSS로 조용히 누락하지 않습니다.
  • 평가된 build-time 모듈이 던지는 오류와 관련 없는 import: styling 값이 필요로 하지 않는 코드는 build time에 실행하지 않고 저자가 쓴 runtime 코드로 남깁니다. 따라서 origin/main 11790bef는 값과 무관한 import가 build 평가 중 throw하면 build가 실패했지만, 이 PR에서는 그 import가 runtime import로 유지되고 throw는 저자 프로그램과 똑같이 runtime에서 일어납니다(browser에서만 동작하는 import도 build를 깨지 않습니다).
  • 정확히 알려진 computed key(const name = 'style'; ve[name](...))는 ve.style과 같은 호출로 정확히 컴파일합니다. 함수 인자나 runtime 값처럼 build time에 알 수 없는 key만 원본 위치의 located 오류입니다(기존 오류 기대값을 정확한 positive로 바꾸고 unknown-key negative를 별도 유지했습니다). 타입으로만 선언된 declare const key: string처럼 TypeScript가 지우는 required input도 원본 file:line:col의 오류입니다.
  • TypeScript 타입 단언은 단언이 없는 표현식과 같게 읽습니다. x as T, x satisfies T, x!, f<T>와 .ts/.css.ts의 <T>x가 대상입니다. 이전에는 <T>x 아래의 저자 setup(loop.self = loop 등)이 누락되고, 일부 소비 지점은 단언을 투명하게 보지 않았습니다. utils::unwrap_syntax_only 두 helper에 TSTypeAssertion arm을 더했고, 이어서 7개 arm(style table unwrap, imported initializer, 상수 접기, namespace demand, escape holder 검색, named/native namespace API 분류, loader demand 읽기)에도 같은 규칙을 적용했습니다. 모든 곳에 as/angle 쌍 회귀를 두었고 arm을 하나씩 제거하면 정확히 그 쌍이 실패함을 확인한 뒤 원래 바이트로 복구했습니다. 같은 소스 누락은 origin/main 11790bef에도 있습니다(소스 증명). Emotion css prop의 숫자→px 변환도 타입 단언을 통과하도록 고쳤습니다(아래 수정 목록).
  • dependency의 실제 compiled output은 entry와 별도의 원래 owner artifact로 누적합니다. 전체 extraction과 ResolverState::check가 성공한 뒤 동일 sheet lock의 owner-aware batch로 한 번 게시합니다. getCss/singleCss에서 theme assignment, body global 및 실제 연결된 keyframes가 유지되고, per-file producer/entry bucket과 empty fallback cleanup도 구분합니다. 실패 시 sheet를 부분 갱신하지 않습니다. public ExtractOutput 및 resolver {path, code} 계약은 바꾸지 않았습니다.
  • Devup css()/Box consumer도 imported native theme 값 및 정확한 helper 결과를 읽습니다. 원래 동적 application 값은 기존 CSS 변수 경로를 따르며 각 값·getter·helper call은 한 번만 읽습니다.
  • Devup은 .css.ts/.css.js를 제자리에서 컴파일합니다. 따라서 vanilla-extract의 serializable-export 규칙보다 넓게 일반 helper, 함수 배열, 순환 객체, symbol 등 직렬화하지 못하는 export를 저자가 작성한 자체 코드로 유지합니다(vanilla-extract 동작을 넘어서는 Devup 동작입니다). alias/default export를 누락하지 않으며, 보존한 initializer가 제거·재작성된 입력을 참조하면 원본 위치에서 오류를 냅니다. blanket Invalid exports 거부는 추가하지 않았고 원래 test_exported_values_become_code를 유지했습니다.
  • fontFace는 생성한 family를 vanilla-extract와 동일하게 따옴표를 포함한 문자열로 반환하고 해당 @font-face를 출력합니다. globalFontFace는 지정한 family를 출력합니다. styleVariants는 실제 for-in처럼 inherited enumerable key, non-enumerable shadowing, 순회 중 삭제·추가·변경 시점을 처리합니다. 유효한 DAG 계약은 지원하고 자기 순환 계약은 유한한 시간 안에 located 오류를 냅니다.

수정한 Crash·Hang·오출력

  • theme-only export의 WASM unreachable panic을 제거했습니다.
  • .tsx 및 .css.ts의 fontFace/globalFontFace unreachable trap을 제거했습니다. 지원하는 native API 16개를 두 종류에서 실제 WASM으로 호출했습니다.
  • 자기 순환 theme contract의 무한 순회를 bounded 원본 위치 오류로 바꿨습니다. 여러 계약 API의 cycle 14개와 valid DAG control을 검증했습니다.
  • 상속된 demand leaf의 빈 class/빈 CSS 조용한 누락과 미호출 method의 this.browser 평가를 수정했습니다.
  • 선택된 table의 누락된 key를 읽는 생성 코드가 runtime에서 TypeError를 던지던 문제(color={({a: first(), b: second()})[key]}에서 key가 없을 때 __devupChoice0['1']). 이 PR의 새 코드가 만든 회귀라 같은 PR에서 고쳤습니다(origin/main은 정상). 선택 payload 읽기가 원래의 optional 접근(?.[)을 유지하며 실제 실행으로 4개 key 경우를 검증합니다.
  • Emotion css prop의 숫자 변환이 css={{padding: 8} as object}에서는 적용되지 않아 32px(Devup 4배 spacing)이 되던 문제를 고쳤습니다. 이제 단언 유무와 관계없이 8px입니다.

새로 생기는 오류

모든 아래 경로는 원본 file:line:col, 원인 및 Fix:를 표시합니다. 정말 필요한 값이 build time에 알려지지 않는 경우에만 거부하며, 관련 없는 runtime 코드를 full evaluation하지 않습니다.

  • 계약의 누락·추가 key, leaf/object 또는 빈 중첩 구조가 다르면 Tokens don't match contract입니다. 전체 형태를 맞추거나 일치하는 하위 계약을 전달합니다. token이 object가 아니면 object token을 전달하도록 안내합니다.
  • fallbackVar의 마지막 fallback 이전 인자가 변수 참조가 아니거나 global contract의 직접 이름/mapper 반환값이 유효한 문자열 이름이 아니면 해당 인자·path를 진단합니다.
  • unknown/forward/erased/nondeterministic required input은 정확한 static 입력 또는 지원하는 CSS 변수 경로로 바꾸도록 안내합니다. type-only import는 runtime import가 필요합니다.
  • native API 값 자체의 runtime escape, exported runtime helper, dynamic namespace 접근, API alias의 mutation/handoff, unscheduled mutable/rebound/aliased input은 원래 사용·변경 위치에서 exact initializer 또는 immutable binding으로 수정하도록 안내합니다. Unicode/CRLF·barrel 경유에서도 원본 owner 위치를 유지합니다.
  • exact capture로 재구성할 수 없는 callable/proxy/accessor/symbol/cycle/exotic/extra-array state, retained-state reconstruction 및 소비·replacement overlap은 plain data, scalar leaf 또는 consumed initializer 안의 정확한 연산으로 바꾸도록 안내합니다. 일반 authored export의 보존과 exact styling capture의 제한은 서로 다른 계약입니다.
  • 보존할 authored fallback이 제거·재작성된 styling 입력에 의존하면 원래 initializer에서 오류를 내며 깨진 코드를 출력하지 않습니다. retained-state setup alias도 정확한 const alias가 필요합니다.
  • fontFace 규칙에 저자가 fontFamily를 넣으면 이를 제거하거나 지정 family용 globalFontFace를 사용하도록 안내합니다. 자기 순환 token은 acyclic finite token object로 수정해야 합니다.
  • strict standalone native API carrier의 named/aliased/default/star/namespace/multihop export는 아직 원본 위치의 오류입니다. 대안은 consuming stylesheet에서 API를 @vanilla-extract/css로부터 직접 import하는 것입니다. changed/ambiguous/failed provenance 진단이 이 일반 거부보다 우선합니다. 같은 raw owner의 서로 다른 compiled payload를 합치거나 stale 값으로 고르지 않습니다.

남는 한계

  • contextual retained carrier/forwarder module 및 artifact CSS asset을 Vite·webpack·Rsbuild·Next(prewarm 포함)·Bun에 전달하는 부분은 #755가 최종화된 뒤 그 위의 별도 후속 PR로 분리했습니다. 이 PR은 core/WASM의 owner별 계산·게시 mechanisms 1~5를 담당합니다. 후속 전달이 완료될 때까지 bundler가 원본 carrier를 standalone으로 요청하면 위 located 오류를 유지하며 어느 모드에서도 CSS를 조용히 잃도록 허용하지 않습니다. #755가 재작성하는 plugin 파일을 이 기능 구현으로 수정하지 않았습니다.
  • 평가된 .css.ts 모듈의 source map은 저자 원본이 아니라 lower된 코드에 대응합니다. libs/extractor/src/lib.rs의 해당 경로는 processed_code가 있으면 저자 원본 remap을 건너뛰며, 코드의 설명은 "A stylesheet's output is generated, so its map stays on that code"입니다. 이 계약은 as/angle/plain 세 형태에 동일하게 적용되는 기존 동작이고, 원본으로 되돌리는 remap은 이 PR의 범위가 아닙니다.
  • recipes/sprinkles(fix(extractor): report vanilla-extract recipes and sprinkles imports under the css alias #718), StyleX(fix(extractor): preserve JavaScript semantics in StyleX dynamic functions #758/fix(extractor): compile StyleX position and view-transition contracts #762), 문서 페이지(W23), 일반 bundler alias·condition·extension·symlink 차이는 범위 밖입니다. 두 모듈의 Devup css(child, later) composition은 #706에서 처리하는 별도 composition 작업이며 이 PR에서 수정하지 않았습니다.
  • 머신 전체 LSP daemon 장애 때문에 LSP clean 결과는 주장하지 않으며 compiler·clippy·tests·실제 hook·Linux CI를 근거로 삼았습니다. 일부 advisory Oracle 결과는 완료 알림 이후 조회가 실패해 얻지 못했고 clean audit로 주장하지 않습니다.

이 PR이 범위 밖으로 둔 기존 동작 (origin/main에서 동일)

private paired 검증이 plain 코드에서 발견한 것으로, origin/main 11790bef에도 같은 결과가 있어 이 PR에서 고치지 않고 별도로 다룹니다.

  • styled.div.withConfig({shouldForwardProp})의 forwarding policy가 $ transient prop($id)을 막지 못하고 나머지 policy 형태(p === 'id', startsWith, includes, validator 등)에서도 모든 prop이 전달됩니다. 원인은 styled_reads.rs의 withheld가 읽거나 policy가 언급한 유한한 이름만 제외하기 때문입니다(11개 fixture).
  • StyleX props([s.a, [s.b]])에서 두 색상 class가 모두 남고 CSS 순서상 앞의 red가 이깁니다(visit.rs의 배열 병합, fix(extractor): preserve JavaScript semantics in StyleX dynamic functions #758/fix(extractor): compile StyleX position and view-transition contracts #762 범위).
  • jsx(Box, {color: first(), ...read()})는 read() spread만 남아 className·CSS 변수가 요소에 붙지 않습니다(평가 순서는 이 PR에서 보존됩니다).
  • 값과 무관한 css() 호출을 .css.ts fallback sandbox에서 실행하면 style()만 있는 API 목록 때문에 not a callable function이 납니다. 이 PR은 위치와 Fix가 있는 오류로 바꿨습니다(origin/main은 위치 없음).
  • 생성된 styled .ts 모듈의 JSX 출력에 저자의 단언이 남는 경우(6개 styled fixture)는 별도 styled .ts JSX 문제로 분리했습니다.

검증

case-058/059 실제 Before·After

베이스 probe와 현재 production tree probe는 실제 WASM으로 수행했고 imported fixture는 실제 theme.css.ts 및 tokens.ts를 resolver로 제공합니다. 아래 A/B/C/D는 실제 출력이며 각 suffix의 결과를 따로 확인했습니다.

모듈 종류 058 Before 058 After 059 Before 059 After
.ts 6:20 vars.colors.bg located error A 7:23 vars.space located error B
.tsx 같은 6:20 error A 같은 7:23 error B
.js 같은 6:20 error A 같은 7:23 error B
.jsx 같은 6:20 error A 같은 7:23 error B
.mjs 같은 6:20 error A 같은 7:23 error B
.css.ts C (기존 성공 경로) A D (앞 base class도 남음) B
.css.js C (기존 성공 경로) A D (앞 base class도 남음) B
A: export const box="f0_0 a"; export const light="theme-0-1";
   .theme-0-1{--colors-bg-0-0:white}.a{background:var(--colors-bg-0-0)}
B: import "./theme.css"; import "./tokens";
   export const button="f0_0 f1_0 b c d";
   .theme-0-0{--space-0-1:8px}.d{color:blue}.a{color:red}
   .c{margin:var(--space-0-1)}.b{padding:8px}
   button에는 a class가 없으므로 producer 원본 red는 적용되지 않음.
C: export const box="a"; export const light="theme-0-1";
   .theme-0-1{--colors-bg-0-0:white}.a{background:var(--colors-bg-0-0)}
D: export const button="c d"+" a b";
   .c{color:blue}.d{margin:var(--space-0-1)}

혼합 058/059도 7종류 모두 같은 styling 결과를 내고, window.document 및 runtime sentinel throw는 원래 코드에 남으면서 build time에는 실행되지 않습니다. 실제 Chromium에서 D1 요소의 computed style을 두 CSS 모드로 검증했습니다.

plain / as / <T>x 쌍 검증

private 쌍 harness가 15개 범주 117개 fixture를 plain·as·angle 세 형태로 실제 WASM(binding, core graph, WASM 세 경로)에서 비교합니다. 최종 결과는 111 PASS / 6 STOP입니다. 6개 STOP은 생성된 styled JSX가 저자의 단언을 그대로 남기는 승인된 TSX 예외(styled attrs 3개, mixin, withComponent, styled read 함수)이며 초록으로 바꾸지 않았습니다.

  • 오류 비교 규칙: 두 형태의 오류는 backtick으로 감싼 인용 구간 하나 안에서만 다를 수 있고 file:line:col, 원인, Fix: 및 나머지 모든 바이트는 같아야 합니다. 인용 구간은 각 형태의 저자 소스에 실제로 있어야 합니다(Oxc codegen이 불필요한 괄호·공백을 지우므로 이 두 가지만 무시). 적용된 4건: value-callee-radix, compose-changed, compose-unknown, forward-unsupported. 위치 규칙: 위치가 달라지는 styled-read-object(plain 2:40, as 2:41, angle 2:49)는 각 위치가 자기 소스 바이트에서 독립적으로 구한 같은 피연산자의 위치와 일치할 때만 허용합니다.
  • 18개 fixture(forwarding 11, 순서·capture·StyleX·impure import·lowering)는 plain-equivalence 기준입니다: 코드 이외 모든 필드, 단언을 남기는 raw 코드는 기존 Oxc-stripped 전체 프로그램, runtime 관찰이 세 형태에서 같고 source map 바이트는 형태마다 자기 저자 소스를 가지므로 교차 비교하지 않는 대신 세 형태가 각자 자기 입력에 대한 기존 map 검증을 통과해야 합니다. 이는 "whole bytes" 완전 일치에서 벗어난 점으로 명시합니다.
  • generated parent 할당 witness와 per-owner 경로 승인을 더해 graph/lowering fixture를 증명했고, fixture의 기대값 4건은 소스에서 도출해 고쳤습니다(vanilla-extract의 숫자 px 규칙에 따른 margin: 3 → 3px, sheet가 한 selector의 선언을 한 rule로 합치는 점, parent artifact가 소유하는 width: 17px, 보존된 @vanilla-extract/css import를 위한 observer 전용 shim). 출력값을 기대값으로 복사하지 않았습니다.
  • 이 검증이 찾은 #761의 회귀 2건(선택 table 누락 key의 runtime TypeError, Emotion css prop 단언)은 각각 회귀 테스트를 먼저 작성해 실패를 확인한 뒤 별도 commit으로 수정했습니다.

Assertion 정합성 수정 내역

검증 기대값을 몰래 변경한 것으로 해석되지 않도록 coordinator가 승인한 수정 범위를 명시합니다. production normalization이나 기존 fixture를 그 기대값에 맞춰 바꾸지 않았습니다.

  • 새 B graph fixture의 올바른 artifact 순서는 [right(empty fallback cleanup), origin]입니다. rootless right를 native producer로 세지 않고 raw filename으로 origin을 정확히 한 번 찾습니다. 기존 CSS decimal normalization에 따라 기대 opacity는 .25/.5입니다. owner·order·empty·duplicate·old immutable payload·active frame linkage를 강화했고 deliberate mutation M1~M7은 실제 실패 후 정확한 원본 byte SHA로 복구했습니다.
  • producer-owned CSS 게시 이후 private driver의 sheet 전체 no red 검사는 D1 정의와 맞지 않아 실제 emitted element class 검사로 바꿨습니다. 범위는 case-059/mixed-059 각 7종류 및 consumer 7종류·union padding·demand consumer·review import·cycle입니다. 모든 fixture와 나머지 property/dependency/identity/error 검사는 유지했고 두 CSS 모드에서 50개 positive, 잘못된 실제 producer class 50개 negative, 복구 50개를 검증했습니다. 원본 matrix SHA256는 E63F34F5020C30EED7EBBA7BFD363EECC66F5AED3C586445A8A06294643C68DB입니다.
  • fix(extractor): preserve JavaScript semantics across values and element calls #756 통합에서 lexical shadowed require는 resolver 호출이 아니므로 원래 fixture를 success/no-shadow-request control로 유지했습니다. 진짜 unbound require는 원본 helper.ts:1:25 오류·cause·Fix·no publication을 확인했습니다.
  • 새 private 기대값이 byte-identical한 as/angle 출력과 어긋나는 경우, 소스 의미가 실제 출력을 설명하면 소스에서 기대값을 다시 도출했고 그렇지 않으면 STOP으로 남겼습니다(예: 타입으로만 선언된 key의 3:15 erased-input 오류 위치는 고정 fixture 바이트에서 독립적으로 구했습니다).

최종 Gate

최신 head는 05094954ef97dd3ae708438b9fe5700c20d9623e이고 CI run 38165975187이 success입니다.

  • publish: lint, cargo tarpaulin --fail-under 100 100.00%(19001/19001 줄), Rust 테스트(extractor 4,377 + css 530 + sheet 149 + wasm 132, 실패 0), Bun 5,535 pass / 0 fail(100% coverage), bun-plugin regression, landing e2e, Codecov upload 모두 성공. 같은 run의 landing-next-e2e, vinext-rsc-css-e2e, benchmark도 성공했습니다. Codecov outage가 아니며 커버리지 제외나 padding은 사용하지 않았습니다.
  • Linux coverage 경로: 98.05% → 99.01%(189줄) → 99.98%(4줄) → 100%. 마지막 4줄은 audit 실패 변환, exported 선언 선택, 빈 style 기본값이었고, 변환 함수와 선언 선택 함수를 실제 parser 입력으로 테스트하고 도달할 수 없는 기본값 분기를 Option 확장으로 접어 해결했습니다.
  • 189줄은 meaningful 테스트(실제 parse된 소스와 extract_with_modules 입력, 실제 located 오류의 file:line:col·원인·Fix 검증)와, 호출자가 이미 제외하는 방어 분기를 반복자·조건식으로 접는 behavior-preserving 정리로 처리했습니다. 정리마다 호출자 file:line과 불변 조건을 적은 증명을 먼저 남겼고(W35s-cov-*.md), 시그니처·정책·기존 출력 byte는 바꾸지 않았습니다. 코드를 제거한 정리는 세 곳입니다: ① capture/emission.rs의 두 번째 capture 통과(첫 통과가 모든 capture에 값을 주거나 located 오류로 끝남), ② 같은 파일의 중복 consumer-literal 거부(앞선 pooled graph 검사가 이미 모든 statement·header를 거부), ③ ordinary_ve/execution.rs의 missing consumer demand view 오류 arm(select_input이 view 없는 consumer에서 Ok(false)로 먼저 반환).
  • 로컬: cargo fmt, Rust 1.99 clippy --workspace --all-targets -D warnings 무경고, workspace 테스트, fresh WASM·root build, 실제 hook(CI unset, 각 commit마다 Rust·Bun 전체와 lint)을 통과했습니다. 모든 commit은 테스트한 whole-file exact tree입니다. Windows tarpaulin 수치는 Linux 100%로 오인하지 않습니다.
  • 최종 head에서 원래 QA 전체 출력(230,071자)이 이전 baseline과 byte 단위로 같고, private 쌍 harness는 117개 중 111 PASS / 6 STOP(승인된 TSX 예외)입니다.
  • 이 PR에서 새로 만든 회귀 2건(선택 table 누락 key의 TypeError, Emotion css prop 단언)은 각각 회귀 테스트가 먼저 실패하는 것을 확인한 뒤 별도 commit(3fbeb00b, 71607620)으로 수정했습니다.

Patch changepack은 포함되어 있습니다(bindings/devup-ui-wasm, packages/plugin-utils, packages/react).

owjs3901 and others added 30 commits October 1, 2026 21:16
…rations

css(a, b) composing classes whose styles the build knows, bound to css() in the file or exported by another module, merges their atoms per property, selector, breakpoint and layer, conditions included, instead of joining classes whose winner the stylesheet order picked. vanilla-extract style([...]) passes each composed style as its own argument, keeping a style composed again later.

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…asurable

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…JSX spreads win

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…led order and JSX element className

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…uate shouldForwardProp at build time

Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ring

Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #691

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… and conditional styles

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…h Emotion

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ed parameters

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ead through namespaces

Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…akes from the visitor

Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…eclared after their use

Refs #695, #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…nding

Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901 and others added 11 commits October 10, 2026 10:29
Refs #690, #693

Cover original-entry native and consumer backedges, real resolver failure and no-native decline, raw CSS exclusion, type-only and syntax-wrapper requests, producer live-read shorthand, strict imported tag receivers, named default identity, and original selected-validation locations. Preserve all20 cases and12 test functions, exact fixtures and every existing oracle. Coordinator approved only NEW V1/V2 setup using real alias edits from the unchanged valid prefix plus exact invalid suffix; wrong alias-offset composition cleancompiled2FAIL and exact restoration passed. Coordinator approved558 NEW normal-formatted pure LOC over8 paths, every newfile below200, after typed setup reuse; no extra cases, packing, exclusions or production changes. Whole tested tree; Rust4937 and Bun5535 pass with fmt/Rust1.99clippy/lint and original real-WASM QA.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add twelve real source-derived carrier, namespace policy, escape and public origin contracts over six paths, 350 NEW normal-formatted pure LOC, no production changes. Coordinator approved ONLY new namespace assertions retaining local ns12..14 on binding.span plus exact fullspecifier7..14/token; deliberate wrong fullspan8..14 failed and exact restoration passed. Coordinator classified the unchanged T1 fixture as invalid TypeScript TS1361 outside styling binding: NEW test now preserves its type-only import, unbound application style call and empty CSS without a located extractor error, explicitly leaving the type error to TypeScript and retaining the alias warning. Every existing oracle/fixture/helper unchanged, no padding or fake metadata. Whole tested tree; Rust4949/Bun5535, fmt/Rust1.99clippy/lint and original real-WASM QA pass.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add seven real Oxc lexical cases for call/apply, computed helper chains, spread callback order, type-only reads, parameter-key Whole demand, and original nested helper escape sites. Preserve all production code and existing fixtures/oracles; new normal source199pureLOC across the inseparable leaf and registration. Qualified Rust4956/Bun5535, Rust1.99 clippy/fmt/lint and unchanged real-WASM QA; deliberate wrong NEW Path expectation failed and exact blob restoration passed all58adjacent cases. CI unset and jobs2 for the single real hook; Windows coverage is not Linux100 authority.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add exactly seven real parser/Semantic private projection and public extractor cases for Path/Whole ordinary receiver demand, TS type-only reads, computed forwarding, conservative spread retention, static8px and original producer failure sites. Coordinator explicitly approved ONLY this same seven-case/seven-path unit ceiling556 new normal pureLOC with every new leaf below200; final555 after assertion-preserving test-only clippy plumbing, no packing or extra cases. Keep production and all existing fixtures/oracles unchanged. Wrong NEW space-to-browser expectedDemand cleancompiled1FAIL, exact restoration passed; Rust4963/Bun5535/fmt/Rust1.99clippy/lint/originalWASMQA qualified. CI unset/jobs2 for ONE real exact-tree hook; Windows coverage is not Linux100 authority.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add seven genuine Boa-value contracts across exactly four tests-only paths and200 normal newpureLOC: null/true/false emitted scalar roundtrip, custom array prototype and symbol-property Unsupported policy, real null-array/sharedDAG shape/descriptor/identity controls, and emitted-root null-array with native CSS red and preserved browser sibling. No fabricated IDs/Shape/State/Engine errors/proxy bypass or production/existingoracle changes. Wrong NEW emittedtrue expectationfalse cleancompiled1FAIL, exactrestorationpassed; seven NEW clippy plumbing repairs retain exact JS bytes and assertions. Qualified Rust4970/Bun5535/fmt/Rust1.99clippy/lint/mixed92/originalWASMQA, ONE CI-unset/jobs2 real whole-tree hook. Windows/targeted coverage is not Linux100 authority; ordinary snapshot Engine invariants and materialization errors remain separate.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Reuse the existing static-key proof with the producer original Semantic for computed initializer names, including recursive objects, without broadening unknown-key or forwarding policy. Regression-first twelve-case suite proved nine original unnecessary-browser failures before the fix and twelve passes after it across all seven module kinds, with unchanged literal and genuine-unknown controls. Exact inseparable five-path unit adds 195 normal pure LOC; original fixtures, oracles, helpers, snapshots and drivers remain unchanged. Qualified full Rust4982, Bun5535 aggregate100, fmt, Rust1.99 clippy, lint, fresh WASM then root and original real-WASM QA before this one real CI-unset hook. Coverage-only property invariant remains a separate later unit; no runtime, public API, resolver, plugin, workflow or cache changes.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Collect every nameable property into an ordered typed vector through the current shared-Semantic key resolver, preserving strict admission, duplicates, own shadows, prototypes, forwarding and original spans while eliminating only impossible post-admission checks. Exactly three unchanged real-parser controls passed before and after the refactor. Approved admission and order mutants cleancompiled one failure each and were restored to exact raw source hashes before the full ordinary748 and workspace4985 green qualification. Exact inseparable two-path unit adds153 normal pure LOC, new leaf136, under explicitly approved250/180 ceilings; original fixtures, oracles, helpers, snapshots and drivers remain unchanged. Qualified fmt, Rust1.99 clippy, fresh WASM then root, Bun5535 aggregate100, lint and all original real-WASM QA before this one real CI-unset hook. No runtime, public API, resolver, plugin, workflow, cache or error policy changes; Linux100 remains a separate required gate.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add six real Parser and full Semantic ownership cases for named defaults, mixed type/value aliases, forwarding, namespace and ordered star exports, and ambient declarations. Preserve original fixtures and oracles; verify one wrong expected span fails and restore the exact original bytes. Tests only, 162 new natural pure LOC, with all repository and unchanged real WASM gates.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Add thirteen real Parser and Semantic cases for eager boundaries, transparent supported wrappers, direct and bound callable expansion, order, uniqueness, and deferred-body controls. Preserve all fixed fixtures and oracles with 177 new natural pure LOC; tests only, all gates and unchanged real WASM QA qualified. The separate angle-bracket assertion authored-setup defect remains unfixed pending its approved source plan.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #693

Preserve authored cyclic export setup through TypeScript angle-bracket assertions by recursively reading the executable operand in both shared helpers. Add eight real-parser and compiled-export regressions with regression-first red, independent arm removal failures, exact restoration, and unchanged existing oracles. Protected five-path source unit adds 234 natural pure LOC; all gates and six full-output as/angle pairs passed. Coordinator explicitly defers the fifteen remaining paired-consumer categories to the successor before PR readiness; evaluated stylesheet maps retain the existing lowered-source contract.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…more places

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
owjs3901 and others added 15 commits October 11, 2026 17:38
…ssing choice

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The barrel and loader demand walks, the consumer execution view and the selector reference scan carried a continue, break or error arm that their callers already exclude. Fold each guard into the iteration so the branch is no longer a line of its own; behavior and output are unchanged.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… lowering edges

Real parsed sources reach the JSX, top-level throw, exported type, mutation, native-name collision, shadowed undefined, overlapping replacement and unresolved native reference paths of the ordinary stylesheet preparation. The unreachable missing-symbol guard of the hygienic import rewrite is folded into its iteration.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…edges

Real parsed sources reach the module.exports and exports alias detection, the readonly and mutable alias classification, the erased native input error and the diagnostic parent walk of the native execution. Three guards that their callers already exclude are folded into their iteration.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Real parsed sources reach the dollar-suffixed native import names, the located failed-check throw, the type-only import error, the missing vanilla-extract alias error, the demand member path rendering and the var and resource declaration slices of the native execution. The replacement copy loop no longer carries a skip branch of its own.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Real parsed sources reach the empty destructuring placeholders, the void and IIFE wrappers, the consecutive declaration removal, the default and namespace import specifiers, the type-only native API import error and the API policy and usage classification of the native selection. Guards that their callers already exclude are folded into their iteration.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Real parsed sources reach the call graph, closure, demand render, demand view, escape audit and static key paths of the native selection, including the erased-by-TypeScript required input, resource declaration and callable errors. Guards that their callers already exclude are folded into their iteration.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…et compile edges

Real parsed sources reach the JSX function and fragment detection of the ordinary dispatch, the empty stylesheet compile, the selector rule rewrite and the escaped selector scan of the style references. The global style selector update is one conditional instead of a match whose other arms did nothing, and two test-helper branches are folded into assertions and an iterator filter.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…dges

Real parsed sources reach the default and namespace import specifiers, the standalone imports, the retained-state alias error and the tagged template and JSX element name spans of the authored stylesheet analysis. Two guards that their callers already exclude are folded into their iteration.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Real native results with null prototypes, accessors, class instances and symbols reach the prototype definition, the located capture errors and the emission error messages. The second capture pass and the duplicate consumer literal check are removed because the first pass already assigns every capture or returns its located error, and the pooled graph check already rejects every statement or header.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…terialization

Real retained native inputs mutated through callables, accessors and proxies, retained bindings that cannot be captured, shared child identity and a terminal result that cannot be materialized now reach their located errors and the engine error conversion. The terminal graph error match is one arm that keeps both outcomes.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Real exported and local declarations reach the owner compile of the demand runtime. The initialization checks of the final session, the mutation audit and the owner finalization keep their error messages and are written as single conditional expressions instead of branches that no schedule can skip.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… inputs

Real stylesheets that export through ES star exports and wrapped CommonJS modules reach the spread export collection, the authored star fallback and the empty operand default of the stylesheet execution. The execution input, the native alias scan and the barrel terminal lookup no longer carry guards their callers exclude.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The audit failure conversion is a single function that passes a script error through and reports a located failure as a type error, with tests for both. The declarators of a top-level or exported declaration are selected by one helper, tested on real parsed statements. The empty style default of the lowered css call no longer has a branch of its own.

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • A project module re-exporting Devup UI (export { Box, css } from '@devup-ui/react', export * from '@devup-ui/react', export { styled as s }, export * as Devup, a default export of an import, chains of barrels and the compat entry) now works: a file importing Box or css from it compiles exactly as if it imported from the package, following the barrel through the resolver (relative paths, tsconfig paths, package exports), and hasDevupUI sees such a file. Members read from a namespace or default import of the package (Devup['css'], const { css } = Devup, Devup.css as a value) compile where the build can follow them, Devup.x for a name the package compiles being read like the named import. New build errors, located at the use: what a barrel re-exports but the build cannot read (an unresolvable export * or module, a namespace of modules re-exporting the package) is an error where its name is used instead of a runtime crash, and a namespace member read by a key the build cannot know (Devup[key]), written, or destructured in a form it cannot follow (a rest, a computed key, a default value, a nested pattern, an exported declaration, or members mixed with ones that stay at runtime) is an error that names the members to read. A namespace passed on whole (Object.keys(Devup), spyOn(Devup, 'css'), export default Devup) is left as it is
  • css(a, b) composing classes whose styles the build knows (a const bound to css() in the file, or a css() result another module exports with a rule object every value of which is known) merges their styles: a later part's declaration replaces an earlier one's for the same property, selector, breakpoint and layer, also under conditions (css(base, cond && danger)) and for ||/?? parts, where the classes used to be joined and the stylesheet order picked the winner. vanilla-extract style([a, b]) passes each composed style as its own argument, so the later one wins, and a style composed again later (style([a, b, a])) is kept. Classes the build does not know (strings, props, CSS Modules) are kept as they are
  • styled(Base) extending a styled component the file binds to a const renders Base's tag directly with Base's styles composed under its own, so the extension's declarations replace Base's for the same property, selector, breakpoint and layer instead of losing to them by stylesheet order; Base's attrs apply before the extension's. attrs merge as styled-components merges them: className is joined with the caller's, style is merged and other props replace earlier ones. On an element, a className or style spread after the explicit prop now wins, as React merges props in the order they are written
  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion and styled-components component selectors now compile at build time: a styled component the file defines that another style selects (${Child} { ... } in CSS text, [Child] or [&:hover ${Child}] as a rule key) gets a short marker class, which only such components get, and the selector reads it as .marker; a component extending it, or an element inlining it for its css prop, keeps the marker. Selectors written without & now nest under the component as stylis nests them (ul { ... } is & ul, a selector list nests each selector, :hover applies to the component itself) instead of becoming global rules, and rule-object keys starting with ., #, [, *, >, + or ~ are read as nested selectors instead of properties
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.
  • hasDevupUI takes the import aliases and is true for a file extraction changes: one importing a redirected package (@emotion/styled, ...), naming @emotion/react in a @jsxImportSource pragma, or in a project whose tsconfig jsxImportSource is @emotion/react, so the Bun plugin no longer skips files whose only Devup UI is Emotion's css prop or an aliased import. Aliases of Devup UI now compile like the original: export const B = Box, const c = css; export { c as cc }, export const D = Devup and export const C = Devup.css in a barrel (the barrel module exports them as re-exports of the package, and what imports them follows them), export { Box } after an import, export default Box, const D = Devup namespace aliases, and const inner = css declared inside a function. A css() or keyframes() const that a function reads before its declaration compiles. New build error: a css()/keyframes() const read before its declaration runs (outside a function) is located and says to move the declaration above its first read
  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.
  • Report extraction, module-resolution and stylesheet execution failures at their original source locations with the cause and repair. Reject recoverable syntax errors, nondeterministic build-time reads, synthetic descriptor/function-source and legacy accessor reflection, and unsafe stylesheet execution fallbacks instead of silently changing CSS. Preserve module resolver and serialization exceptions, evaluate transitive helper side effects, and prevent placeholder-name collisions from losing styles. Match import package names only at exact or slash-delimited boundaries so sibling data packages remain external. Retain helper CSS loads in evaluation order with deduplicated portable relative specifiers and query suffixes, without CSS content dependencies. Report missing CSS or incompatible path roots as located errors, and reject host-recorded unknown CSS export observations even when caught; unused bindings, object passing and typeof follow default TypeScript/JavaScript import semantics.
  • Resolve compiled styling APIs and components by their lexical binding so shadowed local names, including minified library identifiers, remain ordinary JavaScript and JSX. No new build errors; existing diagnostics now apply only to the compiled binding they describe.
  • Styled components the build generates are wrapped in React's forwardRef, so a ref passed to them reaches the element they render on React 18 as well as React 19; the file imports forwardRef from react when it defines one
  • Styled components no longer pass every prop to the tag they render: $ props, theme and the props their style functions or attrs read are kept away from a tag unless it takes them as attributes, and shouldForwardProp (Emotion options or styled-components withConfig) is evaluated at build time. A shouldForwardProp the build cannot evaluate is a build error naming the file, line and code, with the forms it accepts. An element using a styled component the file defines drops props the component neither reads nor passes on; spread props are passed as written
  • Styled components render what their as prop names, defaulting to the tag or component they were defined with, and pass forwardedAs on as as, as styled-components and Emotion do; as used to reach the rendered tag as an attribute. Component.withComponent(target) on a styled component the file binds to a const builds a component rendering the same styles and attrs as target (a tag name or a component JSX can name), where it used to call a method the generated function does not have and throw
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled
  • Compile vanilla-extract static styling-data modules independently of their script suffix, sharing theme contracts, native API values, deterministic variable references and later-wins static composition. Modules that also hold unrelated runtime or JSX code evaluate only the code a styling value needs, and values imported through other modules, barrels and namespaces are read exactly, including through type assertions written as as or <T>. Preserve registered selector identities independently of declaration atoms, and capture default, aliased and forwarded module exports without replaying initializers. Align theme.css source resolution between evaluation and the import graph, map consumer contract leaves and theme overloads to their real values, return the quoted generated family from fontFace, enumerate styleVariants keys like vanilla-extract, and stop self-cyclic theme contracts instead of hanging; fontFace and globalFontFace no longer trap the WASM. Report located build errors (file, line, column, cause and fix) for unknown, forward, erased or nondeterministic static inputs, native values or helpers that escape their exact initialization slice, mutated or aliased inputs, unrepresentable captures, non-object theme tokens, mismatched theme contracts, invalid non-final fallback variables, invalid global theme variable names and an authored fontFamily inside fontFace instead of emitting incomplete or malformed CSS.

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • hasDevupUI takes the import aliases and is true for a file extraction changes: one importing a redirected package (@emotion/styled, ...), naming @emotion/react in a @jsxImportSource pragma, or in a project whose tsconfig jsxImportSource is @emotion/react, so the Bun plugin no longer skips files whose only Devup UI is Emotion's css prop or an aliased import. Aliases of Devup UI now compile like the original: export const B = Box, const c = css; export { c as cc }, export const D = Devup and export const C = Devup.css in a barrel (the barrel module exports them as re-exports of the package, and what imports them follows them), export { Box } after an import, export default Box, const D = Devup namespace aliases, and const inner = css declared inside a function. A css() or keyframes() const that a function reads before its declaration compiles. New build error: a css()/keyframes() const read before its declaration runs (outside a function) is located and says to move the declaration above its first read

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • Compile vanilla-extract static styling-data modules independently of their script suffix, sharing theme contracts, native API values, deterministic variable references and later-wins static composition. Modules that also hold unrelated runtime or JSX code evaluate only the code a styling value needs, and values imported through other modules, barrels and namespaces are read exactly, including through type assertions written as as or <T>. Preserve registered selector identities independently of declaration atoms, and capture default, aliased and forwarded module exports without replaying initializers. Align theme.css source resolution between evaluation and the import graph, map consumer contract leaves and theme overloads to their real values, return the quoted generated family from fontFace, enumerate styleVariants keys like vanilla-extract, and stop self-cyclic theme contracts instead of hanging; fontFace and globalFontFace no longer trap the WASM. Report located build errors (file, line, column, cause and fix) for unknown, forward, erased or nondeterministic static inputs, native values or helpers that escape their exact initialization slice, mutated or aliased inputs, unrepresentable captures, non-object theme tokens, mismatched theme contracts, invalid non-final fallback variables, invalid global theme variable names and an authored fontFamily inside fontFace instead of emitting incomplete or malformed CSS.

@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.json

Patch

  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • Compile vanilla-extract static styling-data modules independently of their script suffix, sharing theme contracts, native API values, deterministic variable references and later-wins static composition. Modules that also hold unrelated runtime or JSX code evaluate only the code a styling value needs, and values imported through other modules, barrels and namespaces are read exactly, including through type assertions written as as or <T>. Preserve registered selector identities independently of declaration atoms, and capture default, aliased and forwarded module exports without replaying initializers. Align theme.css source resolution between evaluation and the import graph, map consumer contract leaves and theme overloads to their real values, return the quoted generated family from fontFace, enumerate styleVariants keys like vanilla-extract, and stop self-cyclic theme contracts instead of hanging; fontFace and globalFontFace no longer trap the WASM. Report located build errors (file, line, column, cause and fix) for unknown, forward, erased or nondeterministic static inputs, native values or helpers that escape their exact initialization slice, mutated or aliased inputs, unrepresentable captures, non-object theme tokens, mismatched theme contracts, invalid non-final fallback variables, invalid global theme variable names and an authored fontFamily inside fontFace instead of emitting incomplete or malformed CSS.

@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.json

Patch

  • Preserve JavaScript semantics for exact imported enum and Math values including negative zero, lexical constants, binding-aware CommonJS require, React createElement calls, type-only imports, and source-ordered props evaluation with native spread snapshots and CSS-variable fallbacks without a styling runtime. Migrate UI-kit style defaults by pure reordering and test reset CSS through compiled output. Report located build errors for runtime styleOrder values, unknowable whole-object style overrides, opaque selector literals, genuinely unmovable lazy logical suspension, Devup UI namespaces passed whole to runtime code, and immediate style reads in the temporal dead zone of lexical bindings; deferred initialized reads keep folding and pre-assignment var reads remain dynamic.

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@codecov

codecov Bot commented Oct 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
bindings/devup-ui-wasm/src/graph_publication.rs 100.00% <100.00%> (ø)
bindings/devup-ui-wasm/src/lib.rs 100.00% <100.00%> (ø)
bindings/devup-ui-wasm/src/resolver_state.rs 100.00% <100.00%> (ø)
libs/css/src/lib.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/aliases.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/gate.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/namespace_aliases.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/native.rs 100.00% <100.00%> (ø)
libs/extractor/src/barrel/native/aliases.rs 100.00% <100.00%> (ø)
... and 112 more

... and 92 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@owjs3901
owjs3901 marked this pull request as ready for review October 11, 2026 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant